package cn.hh.ymcc.service.impl;

import cn.hh.ymcc.constants.CommonConstants;
import cn.hh.ymcc.constants.YmccError;
import cn.hh.ymcc.constants.dto.RedisRegisterDto;
import cn.hh.ymcc.domain.Login;
import cn.hh.ymcc.domain.User;
import cn.hh.ymcc.domain.UserAccount;
import cn.hh.ymcc.domain.UserBaseInfo;
import cn.hh.ymcc.dto.RegisterDto;
import cn.hh.ymcc.feign.LoginFeignClient;
import cn.hh.ymcc.mapper.UserMapper;
import cn.hh.ymcc.result.JSONResult;
import cn.hh.ymcc.service.IUserAccountService;
import cn.hh.ymcc.service.IUserBaseInfoService;
import cn.hh.ymcc.service.IUserService;
import cn.hh.ymcc.util.AssertUtil;
import cn.hh.ymcc.util.BitStatesUtils;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * <p>
 * 会员登录账号 服务实现类
 * </p>
 *
 * @author huang.hao
 * @since 2025-04-20
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    // 注入redis
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private LoginFeignClient loginFeignClient;  // 注入feign接口

    @Autowired
    private IUserBaseInfoService userBaseInfoService;

    @Autowired
    private IUserAccountService userAccountService;



    /**
     * 用户注册
     *
     * @param dto
     */
    @Override
    @GlobalTransactional // 开启全局事务
    public void register(RegisterDto dto) {
        // 1.创建key对象,获取到手机号
        String key = String.format(CommonConstants.REGISTER_PHONE_PRIFIX, dto.getMobile()); // register:1008611....
        // 2.从redis获取到短信验证码
        Object o = redisTemplate.opsForValue().get(key);
        // 3.使用断言工具非空校验
        AssertUtil.isNotNull(o, YmccError.USER_SMS_EMPTY.getMessage());
        // 4.将从redis中获取到短信验证码对象o 强转成RedisRegisterDto对象
        RedisRegisterDto redisRegisterDto = (RedisRegisterDto) o;
        // 5.使用断言判断,redis中的短信验证码对象和用户输入进来的验证码是否相等
        AssertUtil.isEquals(redisRegisterDto.getCode(), dto.getSmsCode(), YmccError.USER_SMSCODE_NOT_EQUALS.getMessage());
        // 6.查询用户是否手机号注册过,获取到user对象
        User user = selectUserByPhone(dto.getMobile());
        // 7.防御性编程 校验用户是否存在
        AssertUtil.isNull(user, YmccError.USER_EXISTS.getMessage()); // 断言用户为空,不是空就报错

        // 8.保存数据 - 保存到t_login
        // 在service-uaa编写controller接口,调用service保存t_login数据
        // 在service-uaa编写controller编写Feign,调用service调用
        JSONResult jsonResult = savaLogin(dto);
        // 8.1.防御性编程,使用断言判断必须是true
        AssertUtil.isTrue(jsonResult.isSuccess(), jsonResult.getMessage());

        // 8.2.login保存成功了  防御性编程: 再次断言返回的数据不能是空
        AssertUtil.isNotNull(jsonResult.getData(), "登录信息保存失败");
        // 8.3.强转,拿到loginId
        Long loginId = Long.valueOf(jsonResult.getData().toString()); // 将Integer转成字符串在强转Long

        // 9.保存user相关  调用对应的service保存数据即可 t_user  t_user_base_info   t_user_account
        user = saveUser(loginId, dto.getMobile());
        // 9.1断言是否保存成功
        AssertUtil.isNotNull(user.getId(),"用户保存失败");

        // 9.2.保存 userBaseInfo
        initUserInfo(user.getId(),dto.getRegChannel());

        // 9.3.保存userAccount
        inintUserAccount(user.getId());


    }

    /**
     *  添加黑名单
     * @param userId
     */
    @Override
    public void saveblacklist(Long userId) {
        // 1.获取到userId



    }

    /**
     *  保存 t_user_account
     * @param loginId
     */
    private void inintUserAccount(Long loginId) {
        UserAccount userAccount = new UserAccount();
        userAccount.setId(loginId); // 设置id
        userAccount.setCreateTime(new Date().getTime()); // 设置创建时间
        // 保存userAccount
        userAccountService.insert(userAccount);


    }

    /**
     *  保存 t_user_base_info
     */
    private void initUserInfo(Long userId,Integer regChannel) {
        UserBaseInfo info = new UserBaseInfo();
        info.setId(userId); // 用户id
        info.setCreateTime(new Date().getTime()); // 创建时间
        info.setRegChannel(regChannel); // 注册渠道
        // 保存info
        userBaseInfoService.insert(info);


    }

    /**
     *  保存user
     */
    private User saveUser(Long loginId,String mobile) {
        // 获取到user对象
        User user = new User();
        user.setLoginId(loginId); // user对象设置登录信息Id
        user.setPhone(mobile); // user对象设置手机号
        user.setNickName(mobile); // user对象设置名称
        user.setCreateTime(new Date().getTime()); // user对象设置创建时间
        // 设置位状态  注册成功  激活  手机认证
        // 使用工具类BitStatesUtils调用添加方法,加上注册成功和激活
        long bitState = BitStatesUtils.addState(BitStatesUtils.OP_REGISTED, BitStatesUtils.OP_ACTIVED);
        // 使用工具类BitStatesUtils调用添加方法,再加上手机认证
        bitState = BitStatesUtils.addState(bitState, BitStatesUtils.OP_AUTHED_PHONE);
        // 最后用user设置位状态
        user.setBitState(bitState);
        // 保存user
        insert(user);
        return user;

    }

    /**
     * 保存Login
     */
    private JSONResult savaLogin(RegisterDto dto) {
        Login login = new Login(); // 获取到登录信息
        login.setUsername(dto.getMobile()); // 登录信息对象设置名字,从实体类dto传入的参数中获取
        login.setPassword(dto.getPassword()); // 登录信息对象设置密码,从实体类dto传入的参数中获取
        login.setType(Login.TYPE_USER); // 登录信息对象设置登录类型,从实体类dto传入的参数中获取
        return loginFeignClient.save(login); // 调用feign接口添加  远程调用Uaa微服务保存Login数据

    }

    /**
     * 查询用户手机号
     * @param mobile
     * @return
     */
    private User selectUserByPhone(String mobile) {
        // 创建一个Wrapper对象
        Wrapper<User> tWrapper = new EntityWrapper();
        // 使用Wrapper对象,拼接手机号
        tWrapper.eq("phone", mobile);  // where phone = #{mobile}
        return selectOne(tWrapper);

    }
}
